

namespace SharpSoft.Data.DAOS
{
    using SharpSoft.Data.GSQL;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    public class DAO : IDisposable
    {
        public DAO(Database p_db, int p_userid = -1)
        {
            this.db = p_db;
            this.UserId = p_userid;
        }

        protected Database db;
        private SQLBuilder _nsb;
        /// <summary>
        /// 获取当前数据库关联的<see cref="SQLBuilder"/>对象
        /// </summary>
        protected SQLBuilder nsb
        {
            get
            {
                if (_nsb == null)
                {
                    _nsb = db?.CreateNativeSQLBuilder();
                }
                return _nsb;
            }
        }

        /// <summary>
        /// 获取当前数据操作层的SQL语句构建器
        /// </summary>
        public SQLBuilder NativeSQLBuilder
        {
            get { return nsb; }
        }


        protected int UserId;

        public Database Database
        {
            get { return db; }
        }

        protected Stream getResFile(string name)
        {
            Type type = this.GetType();
            var _namespace = type.Namespace;
            System.Reflection.Assembly _assembly = type.Assembly;

            var resourceName = _namespace + "." + name;
            Stream stream = _assembly.GetManifestResourceStream(resourceName);
            return stream;
        }


        /// <summary>
        /// 返回用于创建数据结构的脚本
        /// </summary>
        /// <returns></returns>
        protected virtual void InitSchemaByScriptFile(string schemaFile)
        {
            if (schemaFile == null)
            {
                return;
            }
            var stream1 = getResFile(schemaFile);
            if (stream1 == null)
            {
                throw new Exception($"[{schemaFile}]数据结构资源文件不存在，请检查是否存在此文件且已设置文件生成操作为【嵌入的资源】。");
            }
            using (StreamReader sr = new StreamReader(stream1, Encoding.UTF8))
            {
                try
                {
                    string gsql = sr.ReadToEnd();
                    db.ExecuteNonQuery(new GSQLCommandScript(gsql));
                }
                catch (Exception ex)
                {

                    throw new Exception($"{schemaFile} --> {ex.Message}");
                }

            }
        }

        /// <summary>
        /// 在派生类中重写，提供架构文件的文件名。
        /// 架构脚本文件通常与DAO类型放在同一目录且编译类型为“嵌入的资源”。
        /// 默认的文件名为类型名去掉“DAO”后缀且转换为小写再加上".txt"后缀。
        /// </summary>
        protected virtual string InitScriptFileName
        {
            get => this.GetType().Name.Replace("DAO", "", StringComparison.OrdinalIgnoreCase).ToLower() + ".txt";
        }

        /// <summary>
        /// 初始化数据结构，默认使用提供的SchemaFileName指定数据库结构脚本文件
        /// </summary>
        public virtual void InitSchema()
        {
            if (!string.IsNullOrEmpty(this.InitScriptFileName))
            {
                this.InitSchemaByScriptFile(this.InitScriptFileName);
            }

        }
        /// <summary>
        /// 调用指定DAO类型的初始化脚本
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="database"></param>
        public static void InitSchema<T>(Database database) where T : DAO
        {
            T dao = (T)Activator.CreateInstance(typeof(T), database, -1);
            dao.InitSchema();
        }
        /// <summary>
        /// 通用查询多个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="qcbaction"></param>
        /// <param name="obaction"></param>
        /// <returns></returns>
        public T[] Select<T>(Action<QueryConditionBuilder> qcbaction, Action<OrderBuilder> obaction = null, object paras = null) where T : new()
        {
            var sql = nsb.BuildSelect<T>(qcbaction, obaction);
            return db.ExecuteArray<T>(sql, paras);
        }
        /// <summary>
        /// 通用查询单个对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="qcbaction"></param>
        /// <param name="obaction"></param>
        /// <returns></returns>
        public T SelectOne<T>(Action<QueryConditionBuilder> qcbaction, Action<OrderBuilder> obaction = null, object paras = null) where T : new()
        {
            var sql = nsb.BuildSelect<T>(qcbaction, obaction);
            return db.ExecuteObject<T>(sql, paras);
        }
        /// <summary>
        /// 通用插入
        /// </summary>
        /// <typeparam name="T">模型类型</typeparam>
        /// <typeparam name="TRESULT">主键的数据类型，一般为int32或int64</typeparam>
        /// <param name="data"></param>
        /// <returns>返回插入的数据的自增主键</returns>
        public TRESULT? Insert<T, TRESULT>(T data) where TRESULT : struct
        {
            var sql = nsb.BuildInsert<T>();
            sql += nsb.SelectLastId();
            return db.ExecuteScalar<TRESULT?>(sql, data);
        }
        /// <summary>
        /// 通用插入
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns>返回插入的数据的自增主键</returns>
        public int? Insert<T>(T data)
        {
            var sql = nsb.BuildInsert<T>();
            sql += nsb.SelectLastId();
            return db.ExecuteScalar<int?>(sql, data);
        }
        /// <summary>
        /// 通用更新
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="qcbaction"></param>
        /// <returns></returns>
        public int Update<T>(T data, Action<QueryConditionBuilder> qcbaction)
        {
            var sql = nsb.BuildUpdate<T>(qcbaction);
            return db.ExecuteNonQuery(sql, data);
        }
        /// <summary>
        /// 使用主键更新对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public int Update<T>(T data)
        {
            var qcbaction = new Action<QueryConditionBuilder>(qcb =>
            {
                SharpSoft.Data.ModelDescriptor md = new ModelDescriptor(typeof(T));
                var pks = md.GetPrimaryKey();
                if (pks.Length == 0)
                {
                    throw new Exception("未指定主键的对象不允许直接更新。");
                }
                foreach (var item in pks)
                {
                    qcb.equal(qcb.safeName(item.GetNameInDb()), $"@{item.GetPropertyName()}");
                    qcb.and();
                }
            });
            return Update(data, qcbaction);
        }
        /// <summary>
        /// 通用删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="qcbaction"></param>
        /// <returns></returns>
        public int Delete<T>(Action<QueryConditionBuilder> qcbaction, object paras = null)
        {
            var sql = nsb.BuildDelete<T>(qcbaction);
            return db.ExecuteNonQuery(sql, paras);
        }
        /// <summary>
        /// 使用主键删除数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public int Delete<T>(T model)
        {
            var qcbaction = new Action<QueryConditionBuilder>(qcb =>
            {
                SharpSoft.Data.ModelDescriptor md = new ModelDescriptor(typeof(T));
                var pks = md.GetPrimaryKey();
                if (pks.Length == 0)
                {
                    throw new Exception("未指定主键的对象不允许直接删除。");
                }
                foreach (var item in pks)
                {
                    qcb.equal(qcb.safeName(item.GetNameInDb()), $"@{item.GetPropertyName()}");
                    qcb.and();
                }
            });
            return Delete<T>(qcbaction, model);
        }
        /// <summary>
        /// 使用数据的主键作为查询条件来删除这些数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="models"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public int Delete<T>(IEnumerable<T> models)
        {
            SharpSoft.Data.ModelDescriptor md = new ModelDescriptor(typeof(T));
            var pks = md.GetPrimaryKey();
            if (pks.Length == 0)
            {
                throw new Exception("未指定主键的对象不允许直接删除。");
            }
            var qcbaction = new Action<QueryConditionBuilder>(qcb =>
            {
                int i = 0;
                foreach (var model in models)
                {
                    i++;
                    qcb.other(qcb1 =>
                    {
                        foreach (var item in pks)
                        {
                            var vk = $"@{item.GetPropertyName()}{i}";
                            qcb1.equal(qcb1.safeName(item.GetNameInDb()), vk);
                            qcb1.and();
                        }
                    }).or();
                }
            });
            Dictionary<string, object> dic = new Dictionary<string, object>();
            int i = 0;
            foreach (var model in models)
            {
                i++;
                foreach (var item in pks)
                {
                    var vk = $"@{item.GetPropertyName()}{i}";
                    dic.Add(vk, item.Property.GetValue(model));
                }
            }
            return Delete<T>(qcbaction, dic);
        }
        /// <summary>
        /// 直接执行sql脚本
        /// </summary>
        /// <param name="sql"></param>
        /// <returns>返回受最后一条sql语句影响的行数</returns>
        public int Execute(string sql, object paras = null)
        {
            return db.ExecuteNonQuery(sql, paras);
        }


        public void Dispose()
        {

        }
    }
}
